import json
from collections import OrderedDict
from collections.abc import MutableMapping

from my_typing import *
from utils.serializer_utils.encoding import force_str


class ReturnDict(OrderedDict):
    """
    Return object from `serializer.data` for the `Serializer` class.
    为 `Serializer` 类从 `serializer.data` 返回对象

    Includes a backlink to the serializer instance for renderers to use if they need richer field information.
    如果渲染器需要更丰富的字段信息，则包括一个指向序列化器实例的反向链接
    """

    def __init__(self, *args, **kwargs):
        self.serializer = kwargs.pop("serializer")
        super().__init__(*args, **kwargs)

    def copy(self):
        return ReturnDict(self, serializer=self.serializer)

    def __repr__(self):
        return dict.__repr__(self)

    def __reduce__(self):
        # Pickling these objects will drop the .serializer backlink, but preserve the raw data.
        # 对这些对象进行 pickling 将会丢弃`.serializer`的反向链接，但会保留原始数据

        return dict, (dict(self),)


class ReturnList(list):
    """
    Return object from `serializer.data` for the `SerializerList` class.
    为 `Serializer` 类从 `serializer.data` 返回对象

    Includes a backlink to the serializer instance for renderers to use if they need richer field information.
    如果渲染器需要更丰富的字段信息，则包括一个指向序列化器实例的反向链接
    """

    def __init__(self, *args, **kwargs) -> None:
        self.serializer = kwargs.pop("serializer")
        super().__init__(*args, **kwargs)

    def __repr__(self) -> str:
        return list.__repr__(self)

    def __reduce__(self) -> tuple[type[list], tuple[List]]:
        # Pickling these objects will drop the .serializer backlink, but preserve the raw data.
        # 对这些对象进行 pickling 将会丢弃`.serializer`的反向链接，但会保留原始数据

        return list, (list(self),)


class BoundField:
    """
    A field object that also includes `.value` and `.error` properties.
    一个同时包含 `.value` 和 `.error` 属性的字段对象

    Returned when iterating over a serializer instance, providing an API similar to Django forms and form fields.
    当迭代序列化器实例时，提供类似 Django forms 和 form fields 的 API
    """

    def __init__(
        self,
        field,
        value,
        errors,
        prefix: str = "",
    ) -> None:
        self._field = field
        self._prefix = prefix
        self.value = value
        self.errors = errors
        self.name = prefix + self.field_name

    def __getattr__(self, attr_name: str):
        return getattr(self._field, attr_name)

    @property
    def _proxy_class(self):
        return self._field.__class__

    def __repr__(self) -> str:
        return f"<{self.__class__.__name__} value={self.value} errors={self.errors}>"

    def as_form_field(self):
        value = (
            ""
            if self.value in (None, False)
            else self.value
        )

        return self.__class__(self._field, value, self.errors, self._prefix)


class JSONBoundField(BoundField):
    def as_form_field(self):
        value = self.value

        # 当使用 HTML 表单输入并且输入值无效时，value 将是一个 JSON 字符串，而不是一个 JSON 原始值
        if not getattr(value, "is_json_string", False):
            try:
                value = json.dumps(self.value, sort_keys=True, indent=4)

            except (TypeError, ValueError):
                pass

        return self.__class__(self._field, value, self.errors, self._prefix)


class NestedBoundField(BoundField):
    """
    This `BoundField` additionally implements __iter__ and __getitem__ in order to support nested bound fields.
    This class is the type of `BoundField` that is used for serializer fields.
    """

    def __init__(self, field, value, errors, prefix=""):
        if value in (None, ""):
            value = {}

        super().__init__(field, value, errors, prefix)

    def __iter__(self):
        for field in self.fields.values():
            yield self[field.field_name]

    def __getitem__(self, key):
        field = self.fields[key]
        value = (
            self.value.get(key)
            if self.value
            else None
        )
        error = (
            self.errors.get(key)
            if isinstance(self.errors, dict)
            else None
        )

        if hasattr(field, "fields"):
            return NestedBoundField(field, value, error, prefix=self.name + ".")

        return BoundField(field, value, error, prefix=self.name + ".")

    def as_form_field(self):
        values = {}
        for key, value in self.value.items():
            if isinstance(value, (list, dict)):
                values[key] = value

            else:
                values[key] = (
                    ""
                    if value in (None, False)
                    else force_str(value)
                )

        return self.__class__(self._field, values, self.errors, self._prefix)


class BindingDict(MutableMapping):
    """
    This dict-like object is used to store fields on a serializer.

    This ensures that whenever fields are added to the serializer we call
    `field.bind()` so that the `field_name` and `parent` attributes
    can be set correctly.
    """

    def __init__(self, serializer):
        self.serializer = serializer
        self.fields = OrderedDict()

    def __setitem__(self, key, field):
        self.fields[key] = field
        field.bind(field_name=key, parent=self.serializer)

    def __getitem__(self, key):
        return self.fields[key]

    def __delitem__(self, key):
        del self.fields[key]

    def __iter__(self):
        return iter(self.fields)

    def __len__(self):
        return len(self.fields)

    def __repr__(self):
        return dict.__repr__(self.fields)
